﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Net;
using Netbits.Common.Conditions;

namespace Netbits.Common.General
{
	/// <summary>
	/// Represents converter-functions
	/// </summary>
	public static class Converter
	{
		#region Type converting

		/// <summary>
		/// Returns an object of the specified type and whose value is equivalent to the specified object. 
		/// </summary>
		/// <typeparam name="TValue">The type of object to return.</typeparam>
		/// <param name="Value_">An object that implements the IConvertible interface.</param>
		/// <returns>An object whose type is TValue and whose value is equivalent to Value_.</returns>
		public static TValue ConvertTo<TValue>( object Value_ )
		{
			Condition.Requires( Value_, "Value_" ).IsNotNull();

			return (TValue)Convert.ChangeType( Value_, typeof( TValue ) );
		}

		/// <summary>
		/// Returns an object of the specified type and whose value is equivalent to the specified object. 
		/// </summary>
		/// <typeparam name="TValue">The type of object to return.</typeparam>
		/// <param name="Value_">An object that implements the IConvertible interface.</param>
		/// <param name="DefaultRet_">If any error occur this value is returned</param>
		/// <returns>An object whose type is TValue and whose value is equivalent to Value_.</returns>
		public static TValue ConvertTo<TValue>( object Value_, TValue DefaultRet_ )
		{
			try
			{
				return ConvertTo<TValue>( Value_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		#endregion

		#region Enumeration converting

		/// <summary>
		/// Convert an object to the specified ENUM
		/// </summary>
		/// <typeparam name="TEnum">Type of ENUM to convert</typeparam>
		/// <param name="Object_">The object that represent the convertible ENUM</param>
		/// <returns>The TEnum type of Object_</returns>
		public static TEnum ToEnum<TEnum>( object Object_ )
		{
			EnumConverter ec = new EnumConverter( typeof( TEnum ) );
			if( !ec.IsValid( Object_ ) )
				throw new InvalidEnumArgumentException();

			return (TEnum)ec.ConvertFrom( null, CultureInfo.CurrentCulture, Object_ );
		}

		/// <summary>
		/// Convert an object to the specified ENUM
		/// </summary>
		/// <typeparam name="TEnum">Type of ENUM to convert</typeparam>
		/// <param name="Object_">The object that represent the convertible ENUM</param>
		/// <param name="DefaultRet_">If any error occur this value is returned</param>
		/// <returns>The TEnum type of Object_</returns>
		public static TEnum ToEnum<TEnum>( object Object_, TEnum DefaultRet_ )
		{
			try
			{
				return ToEnum<TEnum>( Object_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		/// <summary>
		/// Convert an ENUM to a string-list 
		/// </summary>
		/// <typeparam name="TEnum">The type of the ENUM</typeparam>
		/// <returns>The string-list with ENUM-Names</returns>
		public static List<string> FromEnum<TEnum>()
		{
			List<string> sRet = new List<string>();
			foreach( string name in Enum.GetNames( typeof( TEnum ) ) )
				sRet.Add( name );
			return sRet;
		}

		#endregion

		#region Time converting

		/// <summary>
		/// Convert the object to a new timespan.
		/// If the Object_ is a long-value this value was interpreted as TimeSpan-Ticks
		/// </summary>
		/// <param name="Object_">The object value to convert.</param>
		/// <returns>new Timespan with Object_ value.</returns>
		public static TimeSpan ToTimeSpan( object Object_ )
		{
			if( Object_.GetType() == typeof( long ) )
				return new TimeSpan( (long)Object_ );

			return TimeSpan.Parse( Object_.ToString(), CultureInfo.CurrentCulture );
		}

		/// <summary>
		/// Convert the object to a new timespan.
		/// If the Object_ is a long-value this value was interpreted as TimeSpan-Ticks
		/// </summary>
		/// <param name="Object_">The object value to convert.</param>
		/// <param name="DefaultRet_">If any error occur this TimeSpan was returned</param>
		/// <returns>new Timespan with Object_ value.</returns>
		public static TimeSpan ToTimeSpan( object Object_, TimeSpan DefaultRet_ )
		{
			try
			{
				return ToTimeSpan( Object_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		/// <summary>
		/// Convert the object to a new DateTime instance.
		/// If the Object_ is a long-value this value was interpreted as DateTime-Ticks
		/// </summary>
		/// <param name="Object_">The object value to convert.</param>
		/// <returns>new DatetTime with Object_ value.</returns>
		public static DateTime ToDateTime( object Object_ )
		{
			if( Object_.GetType() == typeof( long ) )
				return new DateTime( (long)Object_ );

			return DateTime.Parse( Object_.ToString(), CultureInfo.InvariantCulture );
		}

		/// <summary>
		/// Convert the object to a new DateTime instance.
		/// If the Object_ is a long-value this value was interpreted as DateTime-Ticks
		/// </summary>
		/// <param name="Object_">The object value to convert.</param>
		/// <param name="DefaultRet_">If any error occur this DateTime was returned</param>
		/// <returns>new DatetTime with Object_ value.</returns>
		public static DateTime ToDateTime( object Object_, DateTime DefaultRet_ )
		{
			try
			{
				return ToDateTime( Object_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		#endregion

		#region String converting

		/// <summary>
		/// Convert the string to a new Guid instance.
		/// </summary>
		/// <param name="Object_">The string value to convert.</param>
		/// <returns>The new Guid with Object_ value.</returns>
		public static Guid ToGuid( string Object_ )
		{
			return new Guid( Object_ );
		}

		/// <summary>
		/// Convert the string to a new Guid instance.
		/// </summary>
		/// <param name="Object_">The string value to convert.</param>
		/// <param name="DefaultRet_">If any error occur this Guid was returned</param>
		/// <returns>The new Guid with Object_ value.</returns>
		public static Guid ToGuid( string Object_, Guid DefaultRet_ )
		{
			try
			{
				return ToGuid( Object_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		/// <summary>
		/// Convert the string to a new Version instance.
		/// </summary>
		/// <param name="Object_">The string value to convert.</param>
		/// <returns>The new Version with Object_ value</returns>
		public static Version ToVersion( string Object_ )
		{
			return new Version( Object_ );
		}

		/// <summary>
		/// Convert the string to a new Version instance.
		/// </summary>
		/// <param name="Object_">The string value to convert.</param>
		/// <param name="DefaultRet_">If any error occur this Version was returned</param>
		/// <returns>The new Version with Object_ value</returns>
		public static Version ToVersion( string Object_, Version DefaultRet_ )
		{
			try
			{
				return ToVersion( Object_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		/// <summary>
		/// Convert the string to a new IPAddress instance.
		/// </summary>
		/// <param name="Object_">The string value to convert.</param>
		/// <returns>The new IPAddress instance with Object_ value</returns>
		public static IPAddress ToIPAddress( string Object_ )
		{
			IPAddress ret;
			ret = IPAddress.Parse( Object_ );
			return ret;
		}

		/// <summary>
		/// Convert the string to a new IPAddress instance.
		/// </summary>
		/// <param name="Object_">The string value to convert.</param>
		/// <param name="DefaultRet_">If any error occur this IPAddress was returned</param>
		/// <returns>The new IPAddress instance with Object_ value</returns>
		public static IPAddress ToIPAddress( string Object_, IPAddress DefaultRet_ )
		{
			try
			{
				return ToIPAddress( Object_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		/// <summary>
		/// Convert the HEX string to an integer
		/// </summary>
		/// <param name="Object_">The HEX string value to convert.</param>
		/// <returns>The integer value from HEX string.</returns>
		public static int HexToInt( string Object_ )
		{
			return int.Parse( Object_, NumberStyles.HexNumber, CultureInfo.CurrentCulture );
		}

		/// <summary>
		/// Convert the HEX string to an integer
		/// </summary>
		/// <param name="Object_">The HEX string value to convert.</param>
		/// <param name="DefaultRet_">If any error occur this integer was returned</param>
		/// <returns>The integer value from HEX string.</returns>
		public static int HexToInt( string Object_, int DefaultRet_ )
		{
			try
			{
				return HexToInt( Object_ );
			}
			catch
			{
				return DefaultRet_;
			}
		}

		#endregion

		#region Number converter

		/// <summary>
		/// Convert the unsigned integer value to a new IPAddress instance.
		/// </summary>
		/// <param name="Value_">The integer-IP value</param>
		/// <returns>The new instance of IPAddress</returns>
		public static IPAddress ToIPAddress( uint Value_ )
		{
			uint bitmask = 0xff000000;
			byte[] parts = new byte[4];
			for( int i = 0; i < 4; i++ )
			{
				byte masked = (byte)((Value_ & bitmask) >> ((3 - i) * 8));
				bitmask >>= 8;
				parts[i] = masked;
			}

			return new IPAddress( parts );
		}

		/// <summary>
		/// Convert the int value to a new string
		/// </summary>
		/// <param name="Hex_">The integer to convert.</param>
		/// <returns>The new HEX string from integer-value</returns>
		public static string IntToHex( int Hex_ )
		{
			string Hex = Convert.ToString( Hex_, 16 );
			if( Hex.Length != 2 )
				Hex = "0" + Hex;

			return Hex;
		}

		#endregion

		#region IPAddress converter

		/// <summary>
		/// Convert the IPAddres to an unsigned int value.
		/// </summary>
		/// <param name="Ip_">The IPAddress instance to convert</param>
		/// <returns>The integer value from the IPAddress instance.</returns>
		public static uint FromIPAddress( IPAddress Ip_ )
		{
			string[] splitted = Ip_.ToString().Split( '.' );
			UInt32 ip = 0;
			for( int i = 0; i < 4; i++ )
			{
				ip = (ip << 8) + ConvertTo<uint>( splitted[i], 0 );
			}
			return ip;
		}

		#endregion
	}
}
